package org.jetlinks.pro.device.service.task.db;

import lombok.AllArgsConstructor;
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
import org.hswebframework.web.api.crud.entity.PagerResult;
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
import org.jetlinks.pro.device.entity.DeviceMessageTaskEntity;
import org.jetlinks.pro.device.enums.TaskState;
import org.jetlinks.pro.device.service.task.DeviceMessageResponse;
import org.jetlinks.pro.device.service.task.DeviceMessageTask;
import org.jetlinks.pro.device.service.task.DeviceMessageTaskRepository;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Collection;
import java.util.Collections;
import java.util.List;

@Component
@AllArgsConstructor
public class DatabaseDeviceMessageTaskRepository implements DeviceMessageTaskRepository {
    private final ReactiveRepository<DeviceMessageTaskEntity, String> repository;

    @Override
    public Mono<Void> addTasks(List<DeviceMessageTask> tasks) {
        return Flux.fromIterable(tasks)
                   .map(DeviceMessageTaskEntity::of)
                   .as(repository::save)
                   .then();
    }

    @Override
    public Mono<Void> addTask(DeviceMessageTask task) {
        return repository
            .save(DeviceMessageTaskEntity.of(task))
            .then();
    }

    @Override
    public Mono<Void> updateResult(DeviceMessageResponse response) {

        return repository
            .createUpdate()
            .set(DeviceMessageTaskEntity::getUpstream, response.getUpstream())
            .set(DeviceMessageTaskEntity::getState, response.getState())
            .set(DeviceMessageTaskEntity::getLastError, response.getError())
            .set(DeviceMessageTaskEntity::getLastErrorCode, response.getErrorCode())
            .set(DeviceMessageTaskEntity::getSendTimestamp, response.getSendTime())
            .set(DeviceMessageTaskEntity::getReplyTimestamp, response.getReplyTime())
            .when(StringUtils.hasText(response.getId()),
                  update -> update.where(DeviceMessageTaskEntity::getId, response.getId()))
            .when(StringUtils.isEmpty(response.getId()),
                  update -> update.where(DeviceMessageTaskEntity::getDeviceId, response.getDeviceId())
                                  .and(DeviceMessageTaskEntity::getMessageId, response.getMessageId())
                                  .in(DeviceMessageTaskEntity::getState,
                                      TaskState.wait,
                                      TaskState.processing,
                                      TaskState.sendError))
            .execute()
            .then()
            ;
    }

    @Override
    public Mono<PagerResult<DeviceMessageTask>> queryPager(QueryParamEntity query) {

        return repository
            .createQuery()
            .setParam(query)
            .count()
            .flatMap(total -> {
                if (total == 0) {
                    return Mono.just(PagerResult.of(0, Collections.emptyList(), query));
                }
                return repository
                    .createQuery()
                    .setParam(query.rePaging(total))
                    .fetch()
                    .map(DeviceMessageTaskEntity::toTask)
                    .collectList()
                    .map(list -> PagerResult.of(total, list, query));
            });
    }

    @Override
    public Flux<DeviceMessageTask> query(QueryParamEntity query) {
        return repository
            .createQuery()
            .setParam(query)
            .fetch()
            .map(DeviceMessageTaskEntity::toTask);
    }

    @Override
    public Mono<DeviceMessageTask> findById(String id) {
        return repository
            .findById(id)
            .map(DeviceMessageTaskEntity::toTask);
    }

    @Override
    public Mono<Void> changeState(Collection<String> taskId, TaskState state) {
        return repository
            .createUpdate()
            .set(DeviceMessageTaskEntity::getState,state)
            .in(DeviceMessageTask::getId,taskId)
            .execute()
            .then();
    }
}
